package network;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import core.Phrox;

public class Client {
	
	public Peer root;
	Connection serv;
	private boolean clientRunning=false;
	private boolean clientReconnect=false;
	private byte numRetry=0;//number to hold the number of times we have tried to reconnect.

	private long startTime = 0;
	//millisecond start time, using System.curTimeMillis();
	//this is set once we finish connecting to the server.
	
	public Client(Peer p){
		root = p;
	}
	
	public long getStartTime(){
		return startTime;
	}
	

	//pass first connectioon attempt.
	public void run_client(InetAddress attempt, int p){
		//connect the first time by asking the user for ip,
		//after that, if the user wants then reconnect based on redund lists
		boolean b = false;
		int i = -1;
		while( !b && i < numRetry ){
			b = connection_client(attempt,p);
			i++;
		}
		
		i= 0;
		//if (b) we disconnected properly
		while(clientReconnect && !b && getRedundS() != null && getRedundS().length > i){
			String str = getRedundS()[i];
			int port = (int) core.Util.add( str.substring(NetworkVars.inetLen).getBytes() );
			try {
				InetAddress inet = InetAddress.getByAddress(str.substring(0,NetworkVars.inetLen).getBytes());
				b = connection_client(inet,port);
			} catch (UnknownHostException e) {
				System.out.println("Couldn't find host in redundS");
			}
			i++;
		}
	}
	
	/**
	 * Connect to a server at 'inet' on port 'port'
	 * @param inet Server address.
	 * @param port Port to connect to.
	 * @return Returns true if (we connected, and then requested to disconnect, and disconnected)
	 * returns false if( we didn't connect || we dropped the connection || we were disconnected)
	 */
	public boolean connection_client(InetAddress inet, int port){
		if(clientRunning){
			return true;
		}
		clientRunning = true;
		try {
			connect_Client(inet,port);
		} catch (Exception e) {
			e.printStackTrace();
			clientRunning = false;
			return false;
		}
		boolean ret = true;
		Phrox.disconnect = false;
		startTime = System.currentTimeMillis();

		//initialise lag showing thing.
		Phrox.hasLag( (byte)0 );

		while(!core.Phrox.exit && !serv.flagDisconnect){


			try {
				Thread.sleep(NetworkVars.defSleep);
			} catch (InterruptedException e1) {}

			try {

				Message m = null;
				if(serv.msgIn.ready()){

					serv.lastAlive = System.currentTimeMillis();
					m = NetIO.read(serv);

					String tmp = null;
					Message send = null;

					switch(m.getType()){

					case Message.CONNECTION_MSG://Message.CONNECTION_MSG:
						if( m.getText().equals(NetworkingProtocol.DISCONNECT) ){
							serv.flagDisconnect = true;
							serv.localDisconnect = false;
							ret = false;//we didn't request the disconnect
						}else{
							if(NetworkVars.watchData){
								System.out.println("Watching ConnectionMsg from "+serv.user.get()+" w/ "+m.getText());
							}
							Phrox.outln("Bad connection msg: "+serv.user.get());
						}
						break;

					case Message.TEXT_MSG://msgType.TEXT_MSG
						core.Phrox.outln(m.getText());
						root.send(m);
						break;

					case Message.PINGR:

						try {
							long start = Long.parseLong(m.getText());
							long end = System.currentTimeMillis();
							double lapse = (end - start)/1.0;
							tmp = "Lag from server: "+lapse+"ms";
							System.out.println(tmp);
							Phrox.outln(tmp);
						} catch (NumberFormatException e) {
							System.out.println("Spoofed or wrong PingR from: "+serv.user.get());
							if(NetworkVars.watchData){
								System.out.println("Watching PingR from "+serv.user.get()+" w/ "+m.getText());
							}
						}
						break;

					case Message.PINGS:

						//for a ping the contents are the time it was sent, so we just send it back.
						// so that the computer on the other end knows the time lapse.
						send = m;
						send.setType(Message.PINGR);
						System.out.println("PingS from "+serv.user.get());
						if(NetworkVars.watchData){
							System.out.println("Watching PingS from "+serv.user.get()+" w/ "+m.getText());
						}
						break;


					case Message.KEEP_ALIVE:
						serv.aliveNow();
						if(NetworkVars.watchData){
							System.out.println("Watching KeepAlive from "+serv.user.get()+" w/ '"+m.getText()+"'");
						}
						break;

					case Message.S_REDUND:
						//to replace us
						m.setType(Message.REDUND);
						root.send(m);
						root.redund.add( core.Util.split(m.getText(), NetworkVars.inetLen+2) );
						break;

					case Message.REDUND:
						//to replace server we are connected to
						root.redundS.add( core.Util.split(m.getText(), NetworkVars.inetLen+2) );
						break;

					case Message.INFO:
						root.setFlags( (byte)m.getText().charAt(0) );
						serv.user.setName(m.getText().substring(1));
						Phrox.outln("Server known as:"+serv.user.getName());
						break;

					default:
						//these messages arn't handled by the server
						System.out.println("Unknown Msg Type:"+m.getType()+"-"+m.getText()+"");
						break;
					}

					if(send != null){
						serv.addMsg(send);
					}
				}//end of message switch:case

				if( core.Util.timeLapsed(System.currentTimeMillis(), serv.lastAlive, NetworkVars.connection_Timeout )){//see if it's been a minute
					//assume they disconnected, because they timed out.
					serv.flagDisconnect = true;
					if(NetworkVars.debug){
						System.out.println("Disconnecting due to timeout");
					}
					Phrox.outln("Disconnected from server due to timeout.");
					Phrox.disconnect = true;
					NetIO.dropConnection(serv);
					ret = false;
					break;
				}

				int numSend = serv.numSend();
				if(numSend > 0){//if we need to send this connection messages...
					//System.out.println("To send: "+numSend);
					for(Message msgSend : serv.getMsgList()){
						if(msgSend != null){
							NetIO.write(serv,msgSend);
							msgSend = null;//remove the sent message from the list
						}
					}
					serv.lastSent = System.currentTimeMillis();
				}

				if( core.Util.timeLapsed(System.currentTimeMillis(), serv.lastSent, NetworkVars.lag_Timeout)){
					//make sure the connection is kept alive
					root.send(new Message(Message.KEEP_ALIVE,""));
					System.out.println("Keep Alive Msg Sent");

					if( core.Util.timeLapsed(serv.lastAlive, System.currentTimeMillis(), NetworkVars.lag_Timeout+1000)){
						//since the last message we got, if it's been more then our lag threshold.
						//experiencing lag, problems?
						System.out.println("LAG? "+serv.lastAlive+"="+System.currentTimeMillis());
						//core.Phrox.outln("You may be experiencing lag from the server.");
						//don't show the user a message, it'll just spam them if we don't connect.
						Phrox.hasLag((byte)1);
					}else{
						Phrox.hasLag((byte)0);
					}

				}

			} catch (IOException e) {
				e.printStackTrace();
				//socket exception
				break;
			} catch (DisconnectionException e) {
				e.printStackTrace();
			}finally {

			}

		}//End while loop, we are queued for disconnect, are exiting, or dropped the connection

		try {
			Phrox.disconnect = true;
			NetIO.disconnect(serv);
		} catch (Exception e) {/*Ignore this, as we are just making sure that we disconnected*/}
		clientRunning = false;
		return ret;
	}

	public boolean connect_Client(InetAddress inet, int port) throws DisconnectionException, IOException{
		serv = new Connection(root.encr);
		serv.connectionStatus = 0;
		try {
			core.Phrox.outln("Connecting to: "+inet+":"+port);
			System.out.println("C: "+inet.getHostAddress()+":"+port);
			serv.open();
			serv.setSocket(inet,port);
			serv.user = new User(inet,"");
		} catch (UnknownHostException e) {
			core.Phrox.outln("Couldn't find server.");
		} catch (IOException e) {
			core.Phrox.outln("Couldn't connect to server.");
		} finally {
			//if in or out are null, we arn't connected.
			if(serv.stdOut==null || serv.msgIn==null){
				System.err.println("NO Connection, aborting attempt.");
				serv.connectionStatus = -1;
				throw new DisconnectionException();
			}
		}
		System.out.println("Connection established with server");


		//connection loop
		Message in=new Message(Message.CONNECTION_MSG,"")
		, out=new Message(Message.CONNECTION_MSG,"");
		while( serv.netProt.getState() != NetworkingProtocol.CONNECTED || out.getText().equals("ERROR") ){

			out = new Message(Message.CONNECTION_MSG, serv.netProt.protocol( in.getText() ) );
			NetIO.write(serv,out);

			do{
				in = NetIO.read(serv);//drop all messages that arn't connection msgs.
			}while(in.getType() != Message.CONNECTION_MSG);

			if(NetworkVars.showData){
				System.out.println(in.getText());
			}
		}
		serv.connectionStatus = 1;
		if(out.getText().equals("ERROR")){
			//didn't connect
			serv.connectionStatus = -1;
		}
		serv.aliveNow();
		Message m = new Message(Message.TEXT_MSG,NetworkVars.Msg_Format
				+NetworkVars.localUsr.getIp().getHostAddress()
				+" is now known as "+NetworkVars.localUsr.getName());
		root.send(m);
		System.out.println("Changed Name");

		return serv.connectionStatus == 1; 

	}

/*
	public String[] getRedund(){
		return root.redund.getList().clone();//don't return the original
	}
*/
	public String[] getRedundS(){
		return root.redundS.getList().clone();//don't return the original
	}

	
}
